
目标描述捕获有关寄存器和指令的大部分信息。要访问该信息，必须实现M88kRegisterInfo和M88kInstrInfo类。这些类还包含钩子，可以覆盖这些钩子来完成那些太复杂，而无法在目标描述中表达的任务。从M88kRegisterInfo类开始，在M88kRegisterInfo.h文件中声明:

\begin{enumerate}
\item
头文件首先包含从目标描述生成的代码:

\begin{cpp}
#define GET_REGINFO_HEADER
#include "M88kGenRegisterInfo.inc"
\end{cpp}

\item
之后，必须在llvm命名空间中声明M88kRegisterInfo类。我们只重写了几个方法:

\begin{cpp}
namespace llvm {
struct M88kRegisterInfo : public M88kGenRegisterInfo {
    M88kRegisterInfo();

    const MCPhysReg *getCalleeSavedRegs(
        const MachineFunction *MF) const override;

    BitVector getReservedRegs(
        const MachineFunction &MF) const override;

    bool eliminateFrameIndex(
        MachineBasicBlock::iterator II, int SPAdj,
        unsigned FIOperandNum,
        RegScavenger *RS = nullptr) const override;

    Register getFrameRegister(
        const MachineFunction &MF) const override;
};
} // end namespace llvm
\end{cpp}

\end{enumerate}

类的定义存储在M88kRegisterInfo.cpp文件中:

\begin{enumerate}
\item
同样，该文件以包含从目标描述生成的代码开始:

\begin{cpp}
#define GET_REGINFO_TARGET_DESC
#include "M88kGenRegisterInfo.inc"
\end{cpp}

\item
构造函数初始化超类，将保存返回地址的寄存器作为参数传递:

\begin{cpp}
M88kRegisterInfo::M88kRegisterInfo()
    : M88kGenRegisterInfo(M88k::R1) {}
\end{cpp}

\item
然后，调用者保存实现返回的寄存器列表的方法。我们在目标描述中定义了列表，并且只返回该列表:

\begin{cpp}
const MCPhysReg *M88kRegisterInfo::getCalleeSavedRegs(
        const MachineFunction *MF) const {
    return CSR_M88k_SaveList;
}
\end{cpp}

\item
之后，处理保留寄存器，保留的寄存器取决于平台和硬件。r0寄存器包含一个常量值0，因此将其视为保留寄存器，r28和r29寄存器始终保留供链接器使用。最后，r31寄存器用作堆栈指针。这个列表可能取决于函数，由于这个动态行为无法生成:

\begin{cpp}
BitVector M88kRegisterInfo::getReservedRegs(
        const MachineFunction &MF) const {
    BitVector Reserved(getNumRegs());
    Reserved.set(M88k::R0);
    Reserved.set(M88k::R28);
    Reserved.set(M88k::R29);
    Reserved.set(M88k::R31);
    return Reserved;
}
\end{cpp}

\item
若果需要帧寄存器，则使用r30。注意，代码还不支持创建帧。若函数需要一个帧，那么r30也必须在getReservedRegs()方法中标记为保留。因为它在超类中声明为纯虚，所以必须实现这个方法:

\begin{cpp}
Register M88kRegisterInfo::getFrameRegister(
        const MachineFunction &MF) const {
    return M88k::R30;
}
\end{cpp}

\item
类似地，因为其为纯虚函数，所以需要实现eliminateFrameIndex()方法。调用它是为了将操作数中的帧索引替换为正确的值，以便对堆栈上的值进行寻址:

\begin{cpp}
bool M88kRegisterInfo::eliminateFrameIndex(
        MachineBasicBlock::iterator MI, int SPAdj,
        unsigned FIOperandNum, RegScavenger *RS) const {
    return false;
}
\end{cpp}

\end{enumerate}

M88kInstrInfo类有许多钩子方法，可以覆盖它们来完成特殊任务。例如，用于分支分析和重新具体化。现在，只重写expandPostRAPseudo()方法，这个方法中扩展了伪指令RET。

\begin{enumerate}
\item
头文件首先包含生成的代码:

\begin{cpp}
#define GET_INSTRINFO_HEADER
#include "M88kGenInstrInfo.inc"
\end{cpp}

\item
M88kInstrInfo类派生自生成的m88kgenstrinfo类。除了覆写expandPostRAPseudo()方法之外，唯一的其他添加是该类拥有先前定义的类M88kRegisterInfo的实例:

\begin{cpp}
namespace llvm {
class M88kInstrInfo : public M88kGenInstrInfo {
    const M88kRegisterInfo RI;
    [[maybe_unused]] M88kSubtarget &STI;

    virtual void anchor();

public:
    explicit M88kInstrInfo(M88kSubtarget &STI);

    const M88kRegisterInfo &getRegisterInfo() const {
        return RI;
    }

    bool
    expandPostRAPseudo(MachineInstr &MI) const override;
} // end namespace llvm
\end{cpp}
\end{enumerate}

实现存储在M88kInstrInfo.cpp类中:

\begin{enumerate}
\item
与头文件一样，实现从包含生成的代码开始:

\begin{cpp}
#define GET_INSTRINFO_CTOR_DTOR
#define GET_INSTRMAP_INFO
#include "M88kGenInstrInfo.inc"
\end{cpp}

\item
然后，定义anchor()方法，用于将虚参表固定到该文件:

\begin{cpp}
void M88kInstrInfo::anchor() {}
\end{cpp}

\item
最后，在expandPostRAPseudo()方法中展开RET。这个方法是在寄存器分配器运行后调用的，目的是扩展伪指令，伪指令可能仍然与机器码混合在一起。若机器指令MI的操作码是伪指令RET，则必须插入jmp \%r1跳转指令，该指令是退出函数的指令。复制所有表示要返回值的隐式操作数，并删除伪指令。若在代码生成过程中需要其他伪指令，则可以扩展这个函数来扩展:

\begin{cpp}
bool M88kInstrInfo::expandPostRAPseudo(
        MachineInstr &MI) const {
    MachineBasicBlock &MBB = *MI.getParent();

    switch (MI.getOpcode()) {
        default:
            return false;
        case M88k::RET: {
            MachineInstrBuilder MIB =
            BuildMI(MBB, &MI, MI.getDebugLoc(),
                    get(M88k::JMP))
                .addReg(M88k::R1, RegState::Undef);

            for (auto &MO : MI.operands()) {
                if (MO.isImplicit())
                    MIB.add(MO);
            }
            break;
    }
    }
    MBB.erase(MI);
    return true;
}
\end{cpp}

\end{enumerate}

这两个类都有最小的实现。若继续开发目标，需要覆写更多的方法。TargetInstrInfo和TargetRegisterInfo基类中的注释值得一读，可以在llvm/include/llvm/CodeGen目录中找到。

还需要更多的类来运行指令选择。接下来，我们将了解帧的向下转译。

































































